package api

import (
	"fmt"
	"time"
	"net/http"
	"io/ioutil"
	"encoding/json"

	"read.com/wxmp/dao"
)

type WechatError struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

type WechatAccessToken struct {
	WechatError

	Token   string `json:"access_token"`
	Expires int    `json:"expires_in"`
}

var (
	ticker           *time.Ticker      = nil
	cacheAccessToken map[int]string    = make(map[int]string)
	cacheTokenExpire map[int]time.Time = make(map[int]time.Time)
)

func InitAccessToken() {
	if duration := loadAccessToken(); duration > time.Second {
		go startCacheTicker(duration)
	}
}

func GetAccessToken(id int) string {
	if token, find := cacheAccessToken[id]; find {
		if cacheTokenExpire[id].After(time.Now()) {
			return token
		}
	}
	return ""
}

func startCacheTicker(duration time.Duration) {
	ticker = time.NewTicker(duration)
	for {
		if ticker == nil {
			break
		}
		select {
		case <-ticker.C:
			loadAccessToken()
		}
		if ticker == nil {
			break
		}
	}
}

func loadAccessToken() time.Duration {
	duration := time.Duration(0)
	appList := dao.GetAppList()
	for _, obj := range appList {
		token := RequestAccessToken(obj.DevId, obj.DevSecret)
		if token == nil {
			continue
		}
		duration = time.Duration(token.Expires) * time.Second
		cacheAccessToken[obj.Id] = token.Token
		cacheTokenExpire[obj.Id] = time.Now().Add(duration)
	}
	return duration
}

func RequestAccessToken(appId, secret string) *WechatAccessToken {
	urlStr := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
		"&appid=" + appId +
		"&secret=" + secret
	httpClient := &http.Client{}
	resp, err := httpClient.Get(urlStr)
	if err != nil || resp.StatusCode != http.StatusOK {
		fmt.Println("request access token error.", err, urlStr)
		return nil
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("read access token error.", err, urlStr)
		return nil
	}

	token := &WechatAccessToken{}
	if err = json.Unmarshal(body, token); err != nil {
		fmt.Println("decode access token error.", err, urlStr, string(body))
		return nil
	} else if token.ErrCode != 0 {
		fmt.Println("the access token invalid.", urlStr, string(body))
		return nil
	}
	return token
}
